/* Copyright 2014 Jan Wolter */

#include "stack.h"
#include "bitableau.h"
#include "found.h"
#include "solve.h"
#include "dfs.h"
#include "game.h"

#define SORT

char *gamename= "Fortress";
char *gameopt= "B";
Stack *gathered= NULL;
Stack *temp= NULL;

int nplayed= 0;
int nwon= 0;
int nabandoned= 0;
int cardsleft= 0;
int maxoff;
int baserank;
int dealflip;
int chessboard= 0;

void setopt(char c)
{
    switch (c)
    {
	/* Play Chessboard Instead of Fortress */
	case 'C': chessboard++; gamename= "Chessboard"; break;
    }
}

/* This is the main program for setting up the current game */
void exec(int gather, int arif, int over, int cut)
{
    int win;

    /* Could set things like ndeck, nsuit here */

    /* Build and shuffle the deck */
    Stack *shuf= mixcards(arif, over, cut);

    /* Create the foundations */
    superfound= sf_acebase;
    for (int s= 0; s < nsuit; s++)
	makeFound(1, s, +1, 13);

    /* Create the tableau - 2 piles of 6 faceup cards each, and 8 more of 5 */
    dealflip= FLIP_NONE;
    int ntab= 10;
    for (int i= 0; i < ntab; i++)
	makeBiTableau(0,
	    dealflip, shuf, S_SAMESUIT, 1, F_ANYTHING, 0);

    for (;;)
    {
	/* Deal tableau by rows */
	for (int i= 0; i < ntab; i++)
	{
	    Card *c= popStack(shuf);
	    if (c == NULL) goto donedealing;
	    pushStack(stacks+bitableau[i].id, c);
	}
    }
    donedealing:;

    freeStack(shuf);

    /* Print initial state */
    if (verbose > 1) printState(stdout, stacks);

    maxoff= 0;
    win= solve(gather,1);
    if (verbose > 0) printf("maxoff=%d\n",maxoff);
    nplayed++;
    cardsleft+= maxoff;
    if (win == 1)
	nwon++;
    else if (win == 2)
	nabandoned++;

    cleanFound();
    cleanBiTableau();
    cleanStacks();
    cleanVisited();
}

/* Gather up the cards into the global 'gathered' stack. This has to be
 * non-destructive, since we may not be done solving when it is called.
 */
void gatherAll()
{
    if (gathered == NULL)
	gathered= allocStack(ncard);
    else
	gathered->n= 0;

    gatherFound(gathered);
    gatherBiTableau(gathered,0);
    if (gathered->n != ncard)
    {
	printf("Gather Failed - found %d cards\n",gathered->n);
	exit(1);
    }
}

/* How good is a move?
 *
 * Returns 0 if the move is completely safe or manditory. Such moves will not
 * be marked as choice points for backtracking.
 *
 * For other moves, positive values are returned with lower values for better
 * moves.
 *
 * Negative values mean the move should not be done at all.
 *
 * So for Fortress
 *
 *   0 = Any move to the foundation.
 *   1 = Move that empties a tableau column.
 *   2 = Moving a card off a card where it does not fit and building down
 *       (This is an irreversable move, uncovering a card that we haven't seen)
 *   3 = Moving a card off a card where it does not fit and building up
 *   4 = building down
 *   5 = building up
 *   6 = filling spaces
 */
int rateMove(Stack *src, int index1, int len, Stack *dst)
{
    /* Moves to foundation are safe */
    if (dst->type == FOUND)
	return 0;

    /* Moves with same source and destination as previous move are safe -
     * this ensures that we never break up stacks, though we can reverse them.
     */
    if (hist != NULL && (hist->srcid == src-stacks &&
		hist->dstid == dst-stacks))
	return 0;

    /* Must be moving in to a tableau space */

    /* Filling a column */
    if (dst->n == 0)
    {
	/* Make sure we aren't doing a stack move to the only empty column */
	if (len > 1)
	{
	    int otherempty= 0;
	    for (int i= 0; i < nbitableau; i++)
	    {
		Stack *s= stacks+bitableau[i].id;
		if (s != dst && s->n == 0)
		{
		    otherempty= 1;
		    break;
		}
	    }
	    if (!otherempty) return -1;
	}
	return 6;
    }

    /* Emptying a column */
    if (index1 == 0)
	return 1;

    Card *c1= src->c[index1-1], *c2= src->c[index1];
    int buildingup= rankmatch(-1, 1, topStack(dst)->n, c2->n)?0:1;

    /* Uncovering a new card */
    if (!suitmatch(S_SAMESUIT, c1->s, c2->s) ||
	(!rankmatch(-1, 1, c1->n, c2->n) &&
	 !rankmatch(+1, 1, c1->n, c2->n)))
	return 2 + buildingup;
    else
	return 4+buildingup;
}

void printState(FILE *f, Stack *stks)
{
    printFound(f, stks);
    printBiTableau(f, stks);
}

int victory()
{
    int off= nOff();
    if (off > maxoff) maxoff= off;
    return (off == ncard);
}

/* Return a quality rating for the current solution. Bigger is better. Usually
 * it's just cards off. This is used to decide which solution to report in
 * cases where we never win.
 */
int quality()
{
    return nOff();
}

void summary()
{
    printf("Played:    %d games\n",nplayed);
    printf("Won:       %d games (%.2f%)\n",nwon,(float)nwon*100/nplayed);
    printf("Abandoned: %d games (%.2f%)\n",nabandoned,(float)nabandoned*100/nplayed);
    printf("Cards Off: %.2f\n",(float)cardsleft/nplayed);
}

/* Comparision function for stack order normalization in currstate() */
static int cmpstacks(const void *p1, const void *p2)
{
    const Stack *s1= *(const Stack **)p1;
    const Stack *s2= *(const Stack **)p2;
    if (s1->n == 0 || s2->n == 0)
	return s2->n - s1->n;
    int c1= s1->c[0] - deck;
    int c2= s2->c[0] - deck;
    return c2 - c1;
}

/* Return a string that uniquely describes the current state. */
char *currstate()
{
    /* ncards for all the cards on things
     * 12 for list terminators.
     */
    char buf[ncard+30], *p= buf;

    /* List of cards on each tableau pile - probably we should normalize
     * the order of tableau piles, sorting them by the first card on them.
     * Not sure it's worth the effort for Indefatible
     */
#ifdef SORT
    Stack *norm[12];
    for (int k= 0; k < nbitableau; k++)
	norm[k]= stacks + bitableau[k].id;
    /* This seg faults for some reason */
    qsort(norm, nbitableau, sizeof(Stack *), cmpstacks);

    for (int k= 0; k < nbitableau; k++)
    {
	Stack *s= norm[k];
	for (int i= 0; i < s->n; i++)
	    *(p++)= (s->c[i] - deck) + 1;
	*(p++)= ncard + 1; /* The card list terminator */
    }
#else
    for (int k= 0; k < nbitableau; k++)
    {
	Stack *s= stacks + bitableau[k].id;
	for (int i= 0; i < s->n; i++)
	    *(p++)= (s->c[i] - deck) + 1;
	*(p++)= ncard + 1; /* The card list terminator */
    }
#endif
    *p= '\0';

    return strdup(buf);
}
